<?php

abstract class Mapper {

    /**
     * @var QueryExecutor
     */
    protected $queryExecutor;

    /**
     * @var SelectQueryBuilder
     */
    protected $selectQuery;

    /**
     * @var InsertQueryBuilder
     */
    protected $insertQuery;

    /**
     * @var UpdateQueryBuilder
     */
    protected $updateQuery;

    /**
     * @var DeleteQueryBuilder
     */
    protected $deleteQuery;

    /**
     * @var Table
     */
    protected $table;

    /**
     * @var string
     */
    protected $domainModelName;

    public function __construct() {
        $this->queryExecutor = new MySqliQueryExecutor();
    }

    /**
     * @param $id
     * @return mixed
     */
    public function getById($id) {
        $selectQueryBuilder = new SelectQueryBuilder(array('*'), $this->table->getName());
        $selectQueryBuilder->addCriteria(new Criteria('id', Criteria::EQUAL, $id));
        $result = $this->queryExecutor->executeQueryString($selectQueryBuilder->getSQLQuery());
        return $result->fetch_object($this->domainModelName);
    }

    /**
     * @return array
     */
    public function getAll() {
        $selectQueryBuilder = new SelectQueryBuilder(array('*'), $this->table->getName());
        $result = $this->queryExecutor->executeQueryString($selectQueryBuilder->getSQLQuery());
        $foundModelObjects = array();
        while($resultModelObject = $result->fetch_object($this->domainModelName)) {
            $foundModelObjects[] = $resultModelObject;
        }
        return $foundModelObjects;
    }

    /**
     * @var Criteria[]
     * @return array
     */
    public function getByConditions($conditions) {
        $selectQueryBuilder = new SelectQueryBuilder(array('*'), $this->table->getName());
        foreach($conditions as $condition) {
            $selectQueryBuilder->addCriteria($condition);
        }
        $result = $this->queryExecutor->executeQueryString($selectQueryBuilder->getSQLQuery());
        $foundModelObjects = array();
        while($resultModelObject = $result->fetch_object($this->domainModelName)) {
            $foundModelObjects[] = $resultModelObject;
        }
        return $foundModelObjects;
    }

    /**
     * @var Criteria[]
     * @return array
     */
    public function getFirstByConditions($conditions) {
        $selectQueryBuilder = new SelectQueryBuilder(array('*'), $this->table->getName());
        foreach($conditions as $condition) {
            $selectQueryBuilder->addCriteria($condition);
        }
        $result = $this->queryExecutor->executeQueryString($selectQueryBuilder->getSQLQuery());
        return $result->fetch_object($this->domainModelName);
    }

    public function deleteAll() {
        $deleteQueryBuilder = new DeleteQueryBuilder($this->table->getName());
        $this->queryExecutor->executeQueryString($deleteQueryBuilder->getSQLQuery());
    }

    public function truncate() {
        $query = 'TRUNCATE TABLE ' . $this->table->getName() . ';';
        $this->queryExecutor->executeQueryString($query);
    }

    /**
     * @param mixed $domainObject
     */
    public function delete($domainObject) {
        $deleteQueryBuilder = new DeleteQueryBuilder($this->table->getName());
        $deleteQueryBuilder->addCriteria(new Criteria('id', Criteria::EQUAL, $domainObject->id));
        $this->queryExecutor->executeQueryString($deleteQueryBuilder->getSQLQuery());
    }

    /**
     * @param int $id
     */
    public function deleteById($id) {
        $deleteQueryBuilder = new DeleteQueryBuilder($this->table->getName());
        $deleteQueryBuilder->addCriteria(new Criteria('id', Criteria::EQUAL, $id));
        $this->queryExecutor->executeQueryString($deleteQueryBuilder->getSQLQuery());
    }

    /**
     * @param Criteria $condition
     */
    public function deleteByCondition($condition) {
        $deleteQueryBuilder = new DeleteQueryBuilder($this->table->getName());
        $deleteQueryBuilder->addCriteria($condition);
        $this->queryExecutor->executeQueryString($deleteQueryBuilder->getSQLQuery());
    }

    /**
     * @param Criteria[] $conditions
     */
    public function deleteByConditions($conditions) {
        $deleteQueryBuilder = new DeleteQueryBuilder($this->table->getName());
        foreach($conditions as $condition) {
            $deleteQueryBuilder->addCriteria($condition);
        }
        $this->queryExecutor->executeQueryString($deleteQueryBuilder->getSQLQuery());
    }

    /**
     * @param DomainModel $domainObject
     */
    public function update($domainObject) {
        $existingObject = $this->getById($domainObject->id);
        $diffs = $existingObject->getDiff($domainObject);

        $fields = array();
        $values = array();
        foreach($diffs as $field => $value) {
            $fields[] = $field;
            $values[] = $value;
        }

        $updateQueryBuilder = new UpdateQueryBuilder($fields, $this->table->getName(), $values);
        $updateQueryBuilder->addCriteria(new Criteria('id', Criteria::EQUAL, $domainObject->id));
        $this->queryExecutor->executeQueryString($updateQueryBuilder->getSQLQuery());
    }

    /**
     * @param array $fieldArrayWithValues
     * @param Criteria $condition
     */
    public function updateByCondition($fieldArrayWithValues, $condition) {
        $fields = array();
        $values = array();
        foreach($fieldArrayWithValues as $field => $value) {
            $fields[] = $field;
            $values[] = $value;
        }

        $updateQueryBuilder = new UpdateQueryBuilder($fields, $this->table->getName(), $values);
        $updateQueryBuilder->addCriteria($condition);
        $this->queryExecutor->executeQueryString($updateQueryBuilder->getSQLQuery());
    }

    /**
     * @param array $fieldArrayWithValues
     * @param Criteria[] $conditions
     */
    public function updateByConditions($fieldArrayWithValues, $conditions) {
        $fields = array();
        $values = array();
        foreach($fieldArrayWithValues as $field => $value) {
            $fields[] = $field;
            $values[] = $value;
        }
        $updateQueryBuilder = new UpdateQueryBuilder($fields, $this->table->getName(), $values);
        foreach($conditions as $condition)
        {
            $updateQueryBuilder->addCriteria($condition);
        }
        $this->queryExecutor->executeQueryString($updateQueryBuilder->getSQLQuery());
    }

    /**
     * @param DomainModel $domainObject
     */
    public function insert($domainObject) {
        $fieldArray = $domainObject->getFieldArrayWithValues();
        $fields = array();
        $values = array();
        foreach($fieldArray as $field => $value) {
            $fields[] = $field;
            $values[] = $value;
        }
        $insertQueryBuilder = new InsertQueryBuilder($fields, $this->table->getName(),$values);
        $this->queryExecutor->executeQueryString($insertQueryBuilder->getSQLQuery());
    }

}